﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Windows.Media.Media3D;


namespace PlanetsAndOrbs
{
    public partial class MainForm : Form
    {
        private PixelRenderer renderer = new PixelRenderer();
        private Random rand = new Random();
        private Bitmap bitmap;
        int eyedist_;


        public MainForm()
        {
            InitializeComponent();
        }

        private void pbRender_Click(object sender, EventArgs e)
        {
            if (backgroundWorker.IsBusy)
            {
                backgroundWorker.CancelAsync();
            }
            else
            {
                bitmap = new Bitmap(
                    Convert.ToInt32(numWidth.Value),
                    Convert.ToInt32(numHeight.Value));
                eyedist_ = Convert.ToInt32(numEyeDist.Value) / 2;
                backgroundWorker.RunWorkerAsync();
                pbRender.Text = "Stop";
                pbPreview.Enabled = false;
                pbHorizontColor.Enabled = false;
                pbSkyColor.Enabled = false;
                pbSkyImage.Enabled = false;
            }
        }

        private void preview()
        {
            if (backgroundWorker.IsBusy)
            {
                backgroundWorker.CancelAsync();
            }
            else
            {
                bitmap = new Bitmap(320, 240);
                eyedist_ = 320 * Convert.ToInt32(numEyeDist.Value) / Convert.ToInt32(numWidth.Value) / 2; 
                backgroundWorker.RunWorkerAsync();
                pbPreview.Text = "Stop";
                pbRender.Enabled = false;
                pbHorizontColor.Enabled = false;
                pbSkyColor.Enabled = false;
                pbSkyImage.Enabled = false;
            }
        }

        private void pbSelectFog_Click(object sender, EventArgs e)
        {
            dlgSkyColor.ShowDialog();
        }

        /// <summary>
        /// Renders a (partial) view with a preset viewpoint, into a bitmap.
        /// </summary>
        /// <param name="b">Bitmap to which to render.</param>
        /// <param name="viewpoint">Viewpoint from which to render. Direction of the view is always (0, 0, 1).</param>
        /// <param name="xstart">Start x coordinate in the bitmap, to enable partial rendering.</param>
        /// <param name="xend">Ending x coordinate in the bitmap, to enable partial rendering.</param>
        /// <param name="xcenter">Center of the image, corresponds to vector (0, 0, 1).</param>
        /// <returns>Vector representation of the color.</returns>
        private void renderView(Bitmap b, Vector3D viewpoint, int xstart, int xend, int xcenter)
        {
            renderer.ViewPoint = viewpoint;
            int h = b.Height;
            double k = Math.PI * 0.5 / h;
            int ycenter = h / 2;

            for (int y = 0; y < h; ++y)
            {
                backgroundWorker.ReportProgress(y * 1000 / h);

                double ry = (y - ycenter) * k;
                for (int x = xstart; x < xend; ++x)
                {
                    if (backgroundWorker.CancellationPending)
                        return;

                    double rx = (x - xcenter) * k;
                    b.SetPixel(x, y, renderer.render(rx, ry));
                }
            }
        }

        private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            if (cbStereo.Checked)
            {   // rendering separate parts for left and right eyes
                int w = bitmap.Width / 2;
                renderView(bitmap, new Vector3D(-0.2, 3, 0), 0, w, w - eyedist_);
                renderView(bitmap, new Vector3D(0.2, 3, 0), w, bitmap.Width, w + eyedist_);
            }
            else
            {   // rendering a single full-image view
                renderView(bitmap, new Vector3D(0, 3, 0), 0, bitmap.Width, bitmap.Width / 2);
            }
        }

        private void backgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            progress.Value = e.ProgressPercentage;
        }

        private void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            // create a new view with the generated bitmap
            Form f = new FormView(bitmap);
            f.Show();

            // restore the button states
            if (pbPreview.Enabled)
            {
                pbRender.Enabled = true;
                pbPreview.Text = "Render Preview";
            }
            else
            {
                pbPreview.Enabled = true;
                pbRender.Text = "Render Image";
            }

            // enable the save
            pbSave.Enabled = true;
            pbHorizontColor.Enabled = true;
            pbSkyColor.Enabled = true;
            pbSkyImage.Enabled = true;
        }

        private void pbHorizontColor_Click(object sender, EventArgs e)
        {
            dlgHorizontColor.ShowDialog();
            renderer.setWorld(dlgSkyColor.Color, dlgHorizontColor.Color);
        }

        private void pbSkyColor_Click(object sender, EventArgs e)
        {
            dlgSkyColor.ShowDialog();
            renderer.setWorld(dlgSkyColor.Color, dlgHorizontColor.Color);
        }

        private void pbSkyImage_Click(object sender, EventArgs e)
        {
            dlgSkyImage.ShowDialog();
            if (!String.IsNullOrEmpty(dlgSkyImage.FileName))
                renderer.setWorld(new Bitmap(dlgSkyImage.FileName));
        }

        private void pbSave_Click(object sender, EventArgs e)
        {
            if (bitmap != null)
            {
                dlgSaveFile.ShowDialog();
                String file = dlgSaveFile.FileName;
                if (!String.IsNullOrEmpty(file))
                    bitmap.Save(file);
            }
        }

        private void cbStereo_CheckedChanged(object sender, EventArgs e)
        {
            numEyeDist.Enabled = cbStereo.Checked;
        }

        private void pbGenerate_Click(object sender, EventArgs e)
        {
            renderer.regenerateObjects(
                rand,
                Convert.ToInt32(numOrbs.Value),
                Convert.ToInt32(numPlanets.Value),
                Convert.ToInt32(numRipples.Value));
            preview();
        }

        private void pbPreview_Click(object sender, EventArgs e)
        {
            preview();
        }
    }
}
